/*
 * jusb.cpp
 *
 *  Created on: 06.02.2009
 *      Author: fregat
 */

# include "jusb.h"
# include "usb_interface.h"
# include <iostream>
# include <map>

using namespace fregat::microchip::usb;

static fregat::microchip::usb::usbEnvironment usbenv;


void
fregat::microchip::usb::usbEnvironment::collect_according_vendor (bus *pb, vendor_t v, productid_t p, std::list <device *> &result)
{
	try {
		device *pd = pb->findDevice(v,p);
	if (pd)
		result.push_back(pd);
	}
	catch (device_not_found &dnfex) {
	}
}

boost::shared_ptr <usb_channel_t>
fregat::microchip::usb::usbEnvironment::findChannel (vendor_t v, productid_t p)
{
	std::cerr << "findChannel : " << "map.size = " << usb_channels.size() << ";" << std::endl;
	for (std::map <int, boost::shared_ptr <usb_channel_t> >::iterator iter = usb_channels.begin();
		iter != usb_channels.end (); ++iter) {
		boost::shared_ptr <usb_channel_t> ch = (*iter).second;
		std::cerr << "ch.vid = " << ch->vid << "; ch.pid = " << ch->pid << ";" << std::endl;
		if (ch->vid == v && ch->pid == p) {
			return ch;
		}
	}
	throw device_not_found (v,p);
}

boost::shared_ptr <usb_channel_t>
fregat::microchip::usb::usbEnvironment::findChannel(usb_device_descriptor_t d)
{
	for (std::map <int, boost::shared_ptr <usb_channel_t> >::iterator iter = usb_channels.begin ();
		iter != usb_channels.end (); ++iter) {
		boost::shared_ptr <usb_channel_t> ch = (*iter).second;
		if (ch->id = d)
			return ch;
	}
	throw channel_not_found (d);
}


fregat::microchip::usb::jniInterface::jniInterface(JNIEnv *penv_, jobject jo_) :
	penv(penv_), jo(jo_)
{
}

void
fregat::microchip::usb::jniInterface::doWriteLog(jLogger *pjl, logLevel_t ll, std::string ss)
{
	std::cerr << "jniInterface::doWriteLog :" << __FILE__ << ":" << __LINE__ << std::endl;
	jclass clazz = penv->FindClass(pjl->getClassName().c_str());
	std::cerr << "jniInterface::doWriteLog :" << __FILE__ << ":" << __LINE__ << std::endl;
	jmethodID mid = 0;
	if (ll == logInterface::INFO)
		mid = penv->GetMethodID(clazz, pjl->getInfoMethodName().c_str(), "(Ljava/lang/String;)V");
	else if (ll == logInterface::DEBUG){
		std::cerr << "jniInterface::doWriteLog :" << __FILE__ << ":" << __LINE__ << std::endl;
		mid = penv->GetMethodID(clazz, pjl->getDebugMethodName().c_str(), "(Ljava/lang/String;)V");
		std::cerr << "jniInterface::doWriteLog :" << (int)mid << __FILE__ << ":" << __LINE__ << std::endl;
	}
	else if (ll == logInterface::FAULT)
		mid = penv->GetMethodID(clazz, pjl->getErrorMethodName().c_str(), "(Ljava/lang/String;)V");
	else {
		std::cerr << "jniInterface::doWriteLog :" << (int)mid << " is unknown. " << __FILE__ << ":" << __LINE__ << std::endl;
		std::stringstream ss;
		ss << __FILE__ << ":" << __LINE__ << ". Loglevel " << ll << " is unknown." << std::endl;
		usbenv.throwException(exceptionInterface::INVALID_LOG_LEVEL, ss.str());
	}
	if (mid == 0) {
		usbenv.throwException(exceptionInterface::INVALID_METHOD_ID, "Method was not found");
	}
	penv->CallObjectMethod(jo, mid, penv->NewStringUTF(ss.c_str()));
}

void
fregat::microchip::usb::jniInterface::doThrowException(jException *pjex,
		exceptionInterface::exceptionID_t eid, std::string s)
{
	class_name_t cn = pjex->getExceptionClassName(eid);
	std::cerr <<  "doThrowException: " << cn << ": " __FILE__ << "." << __LINE__ << std::endl;
	jclass clazz = penv->FindClass(cn.c_str());
	std::cerr <<  "doThrowException: " << s << ": " << __FILE__ << "." << __LINE__ << std::endl;
	penv->ThrowNew(clazz, s.c_str());
}


JNIEXPORT void JNICALL
Java_fregat_microchip_usb_jusb_readConfiguration (JNIEnv *env, jobject jo)
{
	jniBindings jb(env,jo);
	jb.readConfiguration ();
}

JNIEXPORT jobject JNICALL
Java_fregat_microchip_usb_jusb_findDevice (JNIEnv *env, jobject jo, jshort v, jshort p)
{
	jint descriptor = -1;
	jobject jres = 0;
	jniBindings jb(env,jo);
	std::cerr <<  "Find device mit vendor = " << std::hex << v << "; product = " << p << "." __FILE__ << "." << __LINE__ << std::endl;
	jb.debug ("Begin to find device");
	try {
		descriptor = jb.findDevice(v,p);
		jres = jb.createUsbDeviceObject("fregat/microchip/usb/device/usb_device", descriptor);
		if (jres)
			std::cerr <<  "OK." << __FILE__ << "." << __LINE__ << std::endl;
	}
	catch (device_not_found &dnfex) {
		std::stringstream ss;
		ss << "Device with vendor 0x" << std::hex << v << " and product id 0x" << p << " not found" << std::endl;
		std::cerr <<  "Exception." << __FILE__ << "." << __LINE__ << std::endl;
		jb.throwException(exceptionInterface::DEVICE_NOT_FOUND, ss.str());
	}
	return jres;
}

JNIEXPORT jint JNICALL
Java_fregat_microchip_usb_jusb_openDevice (JNIEnv *penv, jobject jo, jobject descr_)
{
	jniBindings jb (penv, jo);
	int descr = int(jb.getIntField(descr_, "device_descriptor"));
	std::stringstream ss;
	ss << __FILE__ << ":" << __LINE__ << ": Descriptor " << int(descr) << std::endl;
	jb.debug(ss.str());
	jb.openDevice(descr);
	return 0;
}

JNIEXPORT jint JNICALL
Java_fregat_microchip_usb_jusb_writeDevice (JNIEnv *penv, jobject jo, jobject descr_, jbyteArray data)
{
	jniBindings jb (penv, jo);
	int descr = int (jb.getIntField (descr_, "device_descriptor"));
	std::stringstream ss;
	ss << __FILE__ << ":" << __LINE__ << ": Descriptor " << int(descr) << std::endl;
	jb.debug(ss.str());
	return jb.writeDevice (descr, data);
}

JNIEXPORT jbyteArray JNICALL
Java_fregat_microchip_usb_jusb_readDevice (JNIEnv *penv, jobject jo, jobject descr_)
{
	jniBindings jb (penv, jo);
	int descr = int (jb.getIntField (descr_, "device_descriptor"));
	return jb.readDevice (descr);
}




fregat::microchip::usb::usbEnvironment::usbEnvironment () :
	plogger (new jLogger ("fregat/microchip/usb/jusb", "info", "debug", "error")),
	pbc (new busCollector())
{
	pexception = new jException();
	pexception->addException(exceptionInterface::DEVICE_NOT_FOUND, "fregat/microchip/usb/device/deviceNotFoundException");
	pexception->addException(exceptionInterface::INVALID_METHOD_ID, "fregat/microchip/usb/invalidMethodException");
	pexception->addException(exceptionInterface::INVALID_LOG_LEVEL, "fregat/microchip/usb/invalidLogleveException");
	pexception->addException(exceptionInterface::CHANNEL_NOT_FOUND, "fregat/microchip/usb/channelNotFoundException");
	pexception->addException(exceptionInterface::DEVICE_NOT_OPENED, "fregat/microchip/usb/device/deviceNotOpenedException");
}

void
fregat::microchip::usb::usbEnvironment::readConfiguration ()
{
	usb_set_debug(50);

	usb_init();

	usb_find_busses();
	usb_find_devices();

	//plogger->info(env, jo, "Hallo, jusb");
	fregat::microchip::usb::bus *pb;
	do {
		pb = new fregat::microchip::usb::bus (*usb_get_busses ());
		pb->readConfiguration ();
		pbc->add(pb);
	} while (pb->getNext());
}

usb_device_descriptor_t
fregat::microchip::usb::usbEnvironment::findDevice(vendor_t v, productid_t p)
{
	usb_device_descriptor_t value = -1;
	try {
		boost::shared_ptr <usb_channel_t> ch = findChannel(v,p);
		value = ch->id;
		std::stringstream ss;
		ss << "Device found in channels list. Descriptor is " << value << std::endl;
		debug(ss.str());
		//env->SetIntField(jres, fid, value);
		return value;
	}
	catch (device_not_found &dnfex) {
		std::cerr <<  "Exception." << __FILE__ << ":" << __LINE__ << std::endl;
		std::stringstream ss;
		ss << "Device not found in channels list." << std::endl;
		debug (ss.str());
	}

	std::list<device *> result;
	pbc->iterateWith(boost::bind(
			&fregat::microchip::usb::usbEnvironment::collect_according_vendor,
			this, _1, v, p, boost::ref (result)));
	std::cerr << "result.size = " << result.size () << ";" << std::endl;
	if (result.size() == 0) {
		std::cerr <<  "throw exception." << __FILE__ << "." << __LINE__ << std::endl;
		std::stringstream ss;
		ss << "Device vendor_id:" << v << ", product_id:" << p << "not found" << std::endl;
		throwException(exceptionInterface::DEVICE_NOT_FOUND, ss.str());
	}

	if (result.size() > 0) {
		device *pd = *result.begin ();
		inpoint_bulk *pin_bulk	= pd->findInBulk();
		outpoint_bulk *pout_bulk = pd->findOutBulk();
		boost::shared_ptr <usb_channel_t> pch(new usb_channel_t ());
		pch->id = usb_channels.size()+1;
		pch->pd = pd;
		pch->outp = pout_bulk;
		pch->pinp = pin_bulk;
		pch->vid = v;
		pch->pid = p;
		pch->state = initial;
		usb_channels[pch->id] = pch;
		value = pch->id;
	}
	std::cerr << "usb_channels.size () = " << usb_channels.size () << std::endl;
	return value;
}

int
fregat::microchip::usb::usbEnvironment::open_device(usb_device_descriptor_t d)
{
	boost::shared_ptr <usb_channel_t> ch = findChannel(d);
	std::stringstream ss;
	ss << __FILE__ << ":" << __LINE__ << ". Channel with descriptor " << d << " found. State " << ch->state << "." << std::endl;
	std::cerr << ss;
	debug(ss.str());
	if (ch->state == error_) {
		std::stringstream ss;
		ss << __FILE__ << ":" << __LINE__ << "Channel with descriptor " << d << " in ERROR state." << std::endl;
		throwException (exceptionInterface::INVALID_STATE, ss.str ());
	}
	if (ch->state == initial || ch->state == closed) {
		ch->pd->open();
		ch->state = opened;
	}
	return 0;
}

int
fregat::microchip::usb::usbEnvironment::write_device (const std::vector <unsigned char> &data, const usb_device_descriptor_t &d)
{
	boost::shared_ptr <usb_channel_t> ch = findChannel (d);
	if (ch->state == error_) {
		std::stringstream ss;
		ss << __FILE__ << ":" << __LINE__ << "Channel with descriptor " << d << " in ERROR state." << std::endl;
		throwException (exceptionInterface::INVALID_STATE, ss.str ());
	}
	if (ch->state == opened)
		ch->outp->write(data, 5000);
	return 0;
}

void
fregat::microchip::usb::usbEnvironment::read_device (std::vector <unsigned char> &data, const usb_device_descriptor_t &d)
{
	boost::shared_ptr <usb_channel_t> ch = findChannel (d);
	if (ch->state == error_) {
		std::stringstream ss;
		ss << __FILE__ << ":" << __LINE__ << "Channel with descriptor " << d << " in ERROR state." << std::endl;
		throwException (exceptionInterface::INVALID_STATE, ss.str ());
	}
	if (ch->state == opened) {
		std::cerr << "Device opened" << std::endl;
		ch->pinp->read(data, 5000);
	}
	else
		std::cerr << "Device not opened" << std::endl;
}



fregat::microchip::usb::jniBindings::jniBindings (JNIEnv *penv, jobject jo)
{

	usbenv.update(penv, jo);
}

jobject
fregat::microchip::usb::jniBindings::createUsbDeviceObject (class_name_t cn, usb_device_descriptor_t i)
{
	return usbenv.createUsbDeviceObject(cn, i);
}

void
fregat::microchip::usb::jniBindings::setIntField (jobject jo, field_name_t fn, int value)
{
	return usbenv.setIntField(jo, fn, value);
}

int
fregat::microchip::usb::jniBindings::getIntField (jobject jo, field_name_t fn)
{
	return usbenv.getIntField(jo, fn);
}

void
fregat::microchip::usb::jniBindings::throwException (exceptionInterface::exceptionID_t eid, std::string s)
{
	usbenv.throwException(eid, s);
}

void
fregat::microchip::usb::jniBindings::debug (std::string s)
{
	usbenv.debug (s);
}

void
fregat::microchip::usb::jniBindings::error (std::string s)
{
	usbenv.error(s);
}
void
fregat::microchip::usb::jniBindings::info (std::string s)
{
	usbenv.info(s);
}

void
fregat::microchip::usb::jniBindings::readConfiguration ()
{
	usbenv.readConfiguration();
}

usb_device_descriptor_t
fregat::microchip::usb::jniBindings::findDevice (vendor_t v, productid_t p)
{
	return usbenv.findDevice(v,p);
}

boost::shared_ptr <usb_channel_t>
fregat::microchip::usb::jniBindings::findChannel(usb_device_descriptor_t d)
{
	return usbenv.findChannel(d);
}

int
fregat::microchip::usb::jniBindings::openDevice(usb_device_descriptor_t d)
{
	try {
		return usbenv.open_device(d);
	}
	catch (channel_not_found &cnf) {
		throwException(exceptionInterface::CHANNEL_NOT_FOUND, cnf.to_string());
	}
	catch (device_not_opened &dno) {
		throwException(exceptionInterface::DEVICE_NOT_OPENED, dno.to_string());
	}
	return -1;
}

int
fregat::microchip::usb::jniBindings::writeDevice(const usb_device_descriptor_t &d, const jbyteArray &data)
{
	int len = usbenv.getByteArrayLength (data);
	char *pdata = usbenv.getByteArrayElement (data, 0);
	std::vector <unsigned char> v ((unsigned char *)pdata, (unsigned char *)(pdata + len));
	std::stringstream ss;
	for (std::vector <unsigned char>::iterator iter = v.begin(); iter != v.end (); ++iter)
		ss << std::hex << "0x" << (short)*iter << " ";
	//for (char *iter = pdata; iter < pdata + len; ++iter)
	//	ss << *iter << " ";
	ss << std::endl;
	debug(ss.str());
	try {
		return usbenv.write_device(v,d);
	}
	catch (config_invalid &cinv) {
		throwException (exceptionInterface::INVALID_CONFIG, cinv.to_string());
	}
	catch (claim_interface_invalid &clinv) {
		throwException (exceptionInterface::DEVICE_NOT_CLAIMED, clinv.to_string());
	}
}

jbyteArray
fregat::microchip::usb::jniBindings::readDevice (const usb_device_descriptor_t &d)
{
	std::vector <unsigned char> result(64);
	usbenv.read_device (result, d);
	std::stringstream ss;
	ss << "Has been read " << result.size () << " bytes " << std::endl;
	//debug (ss.str ());
	for (size_t i = 0; i < result.size(); ++i)
		ss << std::hex << (int)result [i] << " ";
	ss << std::endl;
	debug(ss.str());
	return usbenv.getByteArray (result);
}




